﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Compilation;
using System.Collections;
using System.Web.UI;
using System.IO;

namespace TA.Turbo
{
	/// <summary>
	/// Process requests for .ASPX files.
	/// </summary>
	public class PageHandlerFactory : IHttpHandlerFactory
	{
		#region PUBLIC
		#region METHODS
		/// <summary>
		/// Gets the handler for the specified URL.
		/// </summary>
		/// <param name="context"></param>
		/// <param name="requestType"></param>
		/// <param name="url"></param>
		/// <param name="pathTranslated"></param>
		/// <returns></returns>
		public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
		{
			this._context = context;
			url = url.ToLower();

			Page handler = this.GetPage(url);
			if (handler == null)
			{
				handler = this.CreatePage(url);
			}

			return handler;
		}
		/// <summary>
		/// Adds a released handler back to the pool of handlers.
		/// </summary>
		/// <param name="handler"></param>
		public void ReleaseHandler(IHttpHandler handler)
		{
			if (handler is TPage)
			{
				TPage page = (TPage)handler;
				if (page._initDone && page.IsReusable)
				{
					string url = page._url;
					lock (this.PagePool)
					{
						if (!this.PagePool.ContainsKey(url))
						{
							this.PagePool[url] = new Dictionary<BrowserType, Stack<TPage>>();
						}

						if (!this.PagePool[url].ContainsKey(page.BrowserType))
						{
							this.PagePool[url][page.BrowserType] = new Stack<TPage>();
						}

						this.PagePool[url][page.BrowserType].Push(page);
						//if (page._recompile)
						//{
						//  this.PagePool[url].Clear();
						//  this.PageTypes.Remove(url);
						//}
						//else
						//{
						//  this.PagePool[url].Push(page);
						//}
					}
				}
			}
		}
		#endregion
		#endregion
		#region INTERNAL
		#region CONST/STATIC
		const string PagePoolKey = "TA.Turbo.PageHandlerFactory.Pool",
			PageTypesKey = "TA.Turbo.PageHandlerFactory.PageTypes"
#if DEBUG
, PageTimesKey = "TA.Turbo.PageHandlerFactory.PageTimes"
#endif
;
		#endregion
		#region VARS
		HttpContext _context;
		#endregion
		#region PROPS
		Dictionary<string, Dictionary<BrowserType, Stack<TPage>>> PagePool
		{
			get
			{
				if (this._context.Application[PagePoolKey] == null)
				{
					this._context.Application[PagePoolKey] = new Dictionary<string, Dictionary<BrowserType, Stack<TPage>>>();
				}
				return this._context.Application[PagePoolKey] as Dictionary<string, Dictionary<BrowserType, Stack<TPage>>>;
			}
		}
		//#if DEBUG
		//    Dictionary<string, DateTime> PageTimes
		//    {
		//      get
		//      {
		//        if (this._context.Application[PageTimesKey] == null)
		//        {
		//          this._context.Application[PageTimesKey] = new Dictionary<string, DateTime>();
		//        }
		//        return this._context.Application[PageTimesKey] as Dictionary<string, DateTime>;

		//      }
		//    }
		//#endif
		Dictionary<string, Type> PageTypes
		{
			get
			{
				if (this._context.Application[PageTypesKey] == null)
				{
					this._context.Application[PageTypesKey] = new Dictionary<string, Type>();
				}
				return this._context.Application[PageTypesKey] as Dictionary<string, Type>;

			}
		}
		#endregion
		#region METHODS
		Type GetPageType(string url)
		{
			//#if DEBUG
			//      return BuildManager.GetCompiledType(url);
			//#else
			lock (this.PageTypes)
			{
				if (!this.PageTypes.ContainsKey(url))
				{
					Type type = BuildManager.GetCompiledType(url);
					if (type == null)
						throw new ApplicationException("Page " + url + " not found");

					this.PageTypes[url] = type;
				}
				return this.PageTypes[url];
			}
			//#endif
		}
		TPage GetPage(string url)
		{
			BrowserType browserType = this._context.Request.Browser.GetBrowserType();
			lock (this.PagePool)
			{
				Dictionary<BrowserType, Stack<TPage>> pagesForUrl;
				if (this.PagePool.TryGetValue(url, out pagesForUrl))
				{
#if DEBUG
					lock (this.PageTypes)
					{
						if (this.PageTypes.ContainsKey(url))
						{
							if (!this.PageTypes[url].Equals(BuildManager.GetCompiledType(url)))
							{
								this.PagePool[url].Clear();
								this.PageTypes.Remove(url);
							}
						}
					}
#endif

					Stack<TPage> pagesForUrlBrowserType;
					if (pagesForUrl.TryGetValue(browserType, out pagesForUrlBrowserType))
					{
						if (pagesForUrlBrowserType.Count > 0)
						{
							return pagesForUrlBrowserType.Pop();
						}
					}
				}
			}
			return null;
		}
		Page CreatePage(string url)
		{
			Type pageType = this.GetPageType(url);
			//#if DEBUG
			//      this.PageTimes[url] = DateTime.Now;
			//#endif
			Page wp = (Page)Activator.CreateInstance(pageType);
			if (wp is TPage)
			{
				((TPage)wp)._url = url;
			}
			return wp;
		}
		#endregion
		#endregion
	}
}
